home *** CD-ROM | disk | FTP | other *** search
/ Monster Media 1996 #14 / Monster Media No. 14 (April 1996) (Monster Media, Inc.).ISO / prog_d / oleauttr.zip / NESTOBJ.TXT < prev    next >
Text File  |  1996-01-04  |  7KB  |  184 lines

  1.     *******************************************************************
  2.     Accessing nested OLE object properties with TOLEAutoClient
  3.     *******************************************************************
  4.  
  5. *********************************
  6. NESTED MIRROR CLASSES
  7. *********************************
  8.  
  9.    In cases when an OLE Automation server exposes "nested" objects,
  10. such as in the case of Microsoft Excel and Microsoft Project and
  11. many others, the "mirror" class should be written to contain, as
  12. a member, an instance of the sub-object's mirror class.
  13.  
  14.    Refer to the example and sample code in the accompanying file
  15. GRAPHOLE.ZIP for a simple illustration of the approach, using
  16. Microsoft's MSGRAPH as the server.  Other accompanying example
  17. files also give additional illustrative material for reference.
  18.  
  19.    Before any properties or methods of the sub-object can be accessed,
  20. it will be necessary to execute code to construct the sub-object
  21. instance, as described below.  For additional information and examples,
  22. refer to the zipped example files supplied.  You will need to
  23. consult your server's documentation for information on the
  24. class hierarchy exposed by your server.  For Microsoft applications,
  25. the Developer Network CD-ROM is a useful source of such information.
  26.  
  27.    For example, suppose a server whose ID is "Myserver.Document"
  28. exposes one string property called Name, and also contains a sub-object
  29. called Font which in turn has a string property called Color.  In this case,
  30. the "mirror" classes might look like this:
  31.  
  32. interface
  33.  
  34. uses SysUtils, OleAuto;
  35.  
  36. type
  37.   TDocFont = class(TOleObject)
  38.   private
  39.     function GetColor : String;
  40.     procedure SetColor(str : String);
  41.   public
  42.     property Color : String read GetColor write SetColor;
  43.   end;
  44.  
  45.   TDocument = class(TOleObject)
  46.   private
  47.     fontFont : TDocFont;
  48.     function GetName : String;
  49.     procedure SetName(sName : String);
  50.     function GetFont : TDocFont;
  51.   public
  52.     property Name : String read GetName write SetName;
  53.     property DocFont : TDocFont read GetFont write fontFont;
  54.   end;
  55.  
  56. implementation
  57.  
  58. function TDocument.GetName : String;
  59. const
  60.   pszName : PChar = 'naaaaaammmmmmmme';
  61. begin
  62.   GetOleProperty('Name', 'PChar', pszName);
  63.   Result := StrPas(pszName);
  64. end;
  65.  
  66. procedure TDocument.SetName(sName : String);
  67. const
  68.   pszName : PChar = 'naaaaaammmmmmmme';
  69. begin
  70.   StrPCopy(pszName, sName);
  71.   SetOleProperty('Name', 'PChar', pszName);
  72. end;
  73.  
  74. function TDocument.GetFont : TDocFont;
  75. var
  76.   pIntFont : PInterface;
  77. begin
  78.   GetOleProperty('Font', 'pInterface', pIntFont);
  79.   fontFont := TDocFont.ConnectInterface(pIntFont);
  80.   Result := fontFont;
  81. end;
  82.  
  83. function TDocFont.GetColor : String;
  84. const
  85.   pszColor : PChar = 'collllllooooooooor';
  86. begin
  87.   GetOleProperty('Color', 'PChar', sColor);
  88.   Result := StrPas(pszColor);
  89. end;
  90.  
  91. procedure TDocFont.SetColor(sColor : String);
  92. const
  93.   pszColor : PChar = 'collllllooooooooor';
  94. begin
  95.   StrPCopy(pszColor, sColor);
  96.   SetOleProperty('Color', 'PChar', pszColor);
  97. end;
  98.  
  99.    Pay particular attention to the declaration and implementation of
  100. the DocFont property.  The property is represented as a private
  101. class member (field) of type TDocFont.  Setting the property (its write
  102. access) is accomplished merely by assigning to it, but retrieving the
  103. property (its read access) is accomplished through the TDocument.GetFont
  104. function.  This function has the essential side-effect of calling the
  105. TDocFont constructor ConnectInterface, inherited from TOleObject.  This
  106. is because before any properties of the DocFont sub-object can be accessed,
  107. it must be constructed using a call to its ConnectInterface constructor,
  108. which takes as its argument the pInterface returned by the call to
  109. GetOleProperty that retrieves the pInterface for the OLE server's
  110. DocFont property.
  111.  
  112.    For some servers, you may need to retrieve the pInterface for
  113. a sub-property or collection by using CallOleFunction instead of
  114. GetOleProperty.  Also, the 'pInterface' type defined for this
  115. component may be referred to as 'pDisp', 'LPDISPATCH', 'LPDISP',
  116. or some similar type in your server documentation.  All are
  117. equivalent to a pointer to a low-level OLE IDispatch interface.
  118.  
  119.     In order to access the OLE server properties, you first
  120. need to activate the server by calling its mirror class CreateObject
  121. (or GetObject or GetInProcessObject) constructor.  Then you can access
  122. the properties in the normal way.  A typical code fragment might look like:
  123.  
  124. var
  125.   sMyName, sMyColor : String;
  126.   Document1 : TDocument;
  127. begin
  128.   Document1 := TDocument.CreateObject('myserver.document');
  129.   sMyName := Document1.Name;
  130.   sMyColor := Document1.Font.Color;
  131.   Document1.Release;
  132.         ...
  133. end;
  134.  
  135. *********************************
  136. MEMORY MANAGEMENT - AVOID LEAKAGE
  137. *********************************
  138.  
  139.    Several memory management issues arise when writing mirror classes.
  140. First of all, for simplicity the above example uses Delphi "typed
  141. constants" to hold local string variables.  Despite the name given
  142. to them by Object Pascal (i.e., Delphi), these are not constants
  143. at all, but really static variables to which values can be assigned.
  144. While there is nothing really wrong with using them for this purpose,
  145. it is not really good programming practice, since they are allocated
  146. in the application's data segment and thus consume valuable limited
  147. resources.  A better approach would be to use dynamic allocation
  148. via, for example, the StrAlloc() function.  Of course, each string
  149. allocated in this way must be freed using the StrDispose procedure.
  150. Using this approach, the first function in the above example could
  151. be rewritten as:
  152.  
  153. function TDocument.GetName : String;
  154. var
  155.   pszName : PChar;
  156. begin
  157.   pszName := StrAlloc(256); { Get some space. }
  158.   GetOleProperty('Name', 'PChar', pszName);
  159.   Result := StrPas(pszName);
  160.   StrDispose(pszName); { Free the no-longer-needed space. }
  161. end;
  162.  
  163.    Note that, to be absolutely safe, you could protect the
  164. memory allocation within an exception-handling block.
  165.  
  166.    A more subtle memory allocation issue arises when writing
  167. mirror classes for nested objects.  Within the mirror class,
  168. when you call a nested class ConnectInterface constructor,
  169. Delphi allocates memory dynamically on the heap for the
  170. members of the instance of that class that it creates.
  171. Unless you explicitly keep track of such instances, and
  172. call their destructors, "memory leaks" within Windows
  173. can occur when your application shuts down.  Delphi's
  174. internal memory allocator can sometimes protect you
  175. against this when only a few such constructor calls are
  176. made, but not if they are called repeatedly.  The best
  177. practice is to keep track of these yourself within your
  178. mirror class.  Fortunately, this is easy to do since code
  179. has already been written for you.  One way of doing this
  180. is to write a descendant (or subclass) of TOleObject that
  181. overrides the ConnectInterface constructor in order to
  182. keep track of the relevant object references in a list.
  183. See the code in VISIOOLE.ZIP for an example.
  184.